/**
 * COPYRIGHT HangZhou Trustway Technology Company Limited
 * All right reserved.
 */
package com.robam.cloud.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @Description
 * @Author 薛铁琪
 * @CreateTime 2020-07-29 15:30
 * @Version 1.0
 */
@Slf4j
public class BeanUtil {

    private BeanUtil() {
    }

    /**
     * 复制集合
     *
     * @param <E>
     * @param source
     * @param destinationClass
     * @return
     * @throws InstantiationException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static <E> List<E> copyToList(List<?> source, Class<E> destinationClass) {
        if (source == null || source.size() == 0) return Collections.emptyList();
        List<E> res = new ArrayList<E>(source.size());
        for (Object o : source) {
            E e = null;
            try {
                e = destinationClass.newInstance();
            } catch (InstantiationException ex) {
                log.error("对象转换异常", ex);
                return null;
            } catch (IllegalAccessException ex) {
                log.error("对象转换异常", ex);
                return null;
            }
            BeanUtils.copyProperties(o, e);
            res.add(e);
        }
        return res;
    }

    /**
     * 复制集合
     *
     * @param source
     * @param target
     * @throws BeansException
     */
    public static void copyToObject(Object source, Object target) throws BeansException {
        BeanUtils.copyProperties(source, target);
    }

    public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
        Assert.notNull(object);
        Assert.hasText(propertyName);
        return getDeclaredField(object.getClass(), propertyName);
    }


    public static Field getDeclaredField(Class<?> clazz, String propertyName) throws NoSuchFieldException {
        Assert.notNull(clazz);
        Assert.hasText(propertyName);
        for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(propertyName);
            } catch (NoSuchFieldException e) {

            }
        }
        throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
    }

    public static Object getNameProperty(Object object, String propertyName) {
        Assert.notNull(object);
        Assert.hasText(propertyName);

        Field field = null;
        try {
            field = getDeclaredField(object, propertyName);
        } catch (NoSuchFieldException e1) {
        }
        Object result = null;
        if (null != field) {
            boolean accessible = field.isAccessible();
            field.setAccessible(true);
            try {
                result = field.get(object);
            } catch (Exception e) {
            }
            field.setAccessible(accessible);
        }
        return result;
    }

    public static Object forceGetProperty(Object object, String propertyName) {
        Object result = null;
        try {
            result = getObjValue(object, propertyName, null);
        } catch (Exception e) {
        }
        return result;
    }

    public static void setNameProperty(Object object, String propertyName, Object newValue)
            throws NoSuchFieldException {
        Assert.notNull(object);
        Assert.hasText(propertyName);

        Field field = getDeclaredField(object, propertyName);
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        try {
            field.set(object, newValue);
        } catch (Exception e) {
        }
        field.setAccessible(accessible);
    }


    public static void forceSetProperty(Object object, String propertyName, Object newValue) {
        try {
            setObjValue(object, propertyName, newValue);
        } catch (Exception e) {

        }
    }

    public static Object invokePrivateMethod(Object object, String methodName, Object... params)
            throws NoSuchMethodException {
        Assert.notNull(object);
        Assert.hasText(methodName);
        Class<?>[] types = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            types[i] = params[i].getClass();
        }

        Class<?> clazz = object.getClass();
        Method method = null;
        for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                method = superClass.getDeclaredMethod(methodName, types);
                break;
            } catch (NoSuchMethodException e) {

            }
        }

        if (method == null)
            throw new NoSuchMethodException("No Such Method:" + clazz.getSimpleName() + methodName);

        boolean accessible = method.isAccessible();
        method.setAccessible(true);
        Object result = null;
        try {
            result = method.invoke(object, params);
        } catch (Exception e) {
            ReflectionUtils.handleReflectionException(e);
        }
        method.setAccessible(accessible);
        return result;
    }

    @SuppressWarnings("unused")
    public static Method transferMethoder(String classpath, String methodname, Class<?> types[]) {
        try {
            Class<?> clazz = Class.forName(classpath);
            for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
                return superClass.getMethod(methodname, types);
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    @SuppressWarnings("unused")
    public static Method transferMethoder(Object obj, String methodname, Class<?> types[]) {
        try {
            Class<?> clazz = obj.getClass();
            for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
                return superClass.getMethod(methodname, types);
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    public static Field[] getObjProperty(Object obj) {
        Class<?> c = obj.getClass();
        Field[] field = c.getDeclaredFields();
        return field;
    }


    public static void copySupperPropertys(Object arg0, Object arg1) throws Exception {
        if (null != arg0 && null != arg1) {
            Field[] field = getObjSupperProperty(arg1);
            if (null != field) {
                for (int i = 0; i < field.length; i++) {
                    Object value = BeanUtil.forceGetProperty(arg1, field[i].getName());
                    BeanUtil.forceSetProperty(arg0, field[i].getName(), value);
                }
            }
        } else {
            throw new Exception("");
        }
    }

    public static void copyPropertysInMap(Object arg0, Object arg1, Map<String, String> map) throws Exception {
        if (null != arg0 && null != arg1) {
            Field[] field = getObjProperty(arg1);
            if (null != field) {
                for (int i = 0; i < field.length; i++) {
                    if (map.containsKey(field[i].getName())) {
                        Object value = BeanUtil.forceGetProperty(arg1, field[i].getName());
                        BeanUtil.forceSetProperty(arg0, field[i].getName(), value);
                    }
                }
            }
        } else {
            throw new Exception("");
        }
    }

    public static void copyImplPropertys(Object arg0, Object arg1) throws Exception {
        if (null != arg0 && null != arg1) {
            Field[] field = getObjProperty(arg1);
            if (null != field) {
                for (int i = 0; i < field.length; i++) {
                    Object value = BeanUtil.forceGetProperty(arg1, field[i].getName());
                    BeanUtil.forceSetProperty(arg0, field[i].getName(), value);
                }
            }
        } else {
            throw new Exception("");
        }
    }

    public static Field[] getObjSupperProperty(Object obj) {
        Class<?> c = obj.getClass();
        Class<?> supper = c.getSuperclass();
        List<Field> list = new ArrayList<Field>();
        if (null != supper) {
            for (Class<?> superClass = supper; superClass != Object.class; superClass = superClass.getSuperclass()) {
                Field[] fieldchild = superClass.getDeclaredFields();
                if (null != fieldchild) {
                    for (Field field2 : fieldchild) {
                        list.add(field2);
                    }
                }
            }
        }
        Field[] field = new Field[list.size()];
        field = list.toArray(field);
        return field;
    }

    public static Field[] getObjOpSupperProperty(Object obj) {
        Class<?> c = obj.getClass();
        Class<?> supper = c.getSuperclass();
        List<Field> list = new ArrayList<Field>();
        if (null != supper) {
            for (Class<?> superClass = supper; superClass != Object.class; superClass = superClass.getSuperclass()) {
                Field[] fieldchild = superClass.getDeclaredFields();
                if (null != fieldchild) {
                    for (Field field2 : fieldchild) {
                        list.add(field2);
                    }
                }
            }
        }
        Field[] field = new Field[list.size()];
        field = list.toArray(field);
        return field;
    }

    public static Field[] getObjAllProperty(Object obj) {
        List<Field> list = new ArrayList<Field>();
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            Field[] fieldchild = superClass.getDeclaredFields();
            if (null != fieldchild) {
                for (Field field2 : fieldchild) {
                    list.add(field2);
                }
            }
        }
        Field[] field = new Field[list.size()];
        field = list.toArray(field);
        return field;
    }

    public static Field[] getObjAllOpProperty(Object obj) {
        List<Field> list = new ArrayList<Field>();
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            Field[] fieldchild = superClass.getDeclaredFields();
            if (null != fieldchild) {
                for (Field field2 : fieldchild) {
                    list.add(field2);
                }
            }
        }
        Field[] field = new Field[list.size()];
        field = list.toArray(field);
        return field;
    }

    public static String getProNameMethod(String proName) {
        String methodName = "";
        if (StringUtil.isNotBlank(proName)) {
            methodName = "get" + StringUtil.getFirstUpper(proName);
        }
        return methodName;
    }

    public static String getProSetNameMethod(String proName) {
        String methodName = "";
        if (StringUtil.isNotBlank(proName)) {
            methodName = "set" + StringUtil.getFirstUpper(proName);
        }
        return methodName;
    }

    public static Object getObjValue(Object obj, String name, Object defObj) {
        Object valueObj = null;
        String methodName = getProNameMethod(name);
        Method method = transferMethoder(obj, methodName, new Class[0]);
        if (null != method) {
            try {
                valueObj = method.invoke(obj);
                if (null == valueObj) {
                    valueObj = defObj;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return valueObj;
    }

    public static void setObjValue(Object obj, String name, Object defObj) {
        String methodName = getProSetNameMethod(name);
        try {
            Field field = getDeclaredField(obj, name);
            Class<?> fclass = field.getType();
            Object valueobj = getValueByType(fclass.getName(), defObj);
            Class<?>[] types = {fclass};
            Method method = transferMethoder(obj, methodName, types);
            if (null != method) {
                method.invoke(obj, valueobj);
            }
        } catch (Exception e) {
        }
    }

    public static Object getValueByType(String className, Object defObj) {
        Object obj = null;
        if (className.indexOf("String") >= 0) {
            obj = defObj;
        } else if (className.indexOf("int") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "0";
            }
            obj = Long.valueOf(String.valueOf(defObj)).intValue();
        } else if (className.indexOf("Long") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "0";
            }
            obj = Long.valueOf(String.valueOf(defObj));
        } else if (className.indexOf("Double") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "0";
            }
            obj = Double.valueOf(String.valueOf(defObj));
        } else if (className.indexOf("double") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "0";
            }
            obj = Double.valueOf(String.valueOf(defObj));
        } else if (className.indexOf("Date") >= 0) {
            if (null != defObj && StringUtil.isNotBlank(String.valueOf(defObj))) {
                obj = DateUtil.getDateToString(DateUtil.getDateString((Date) defObj, DateUtil.DATESHOWFORMAT), DateUtil.DATESHOWFORMAT);
            }
        } else if (className.indexOf("Integer") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "0";
            }
            obj = Integer.valueOf(String.valueOf(defObj));
        } else if (className.indexOf("boolean") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "false";
            }
            if ("true".equals(String.valueOf(defObj))) {
                obj = true;
            } else {
                obj = false;
            }
        } else if (className.indexOf("Boolean") >= 0) {
            if (StringUtil.isBlank(String.valueOf(defObj))) {
                defObj = "false";
            }
            if ("true".equals(String.valueOf(defObj))) {
                obj = true;
            } else {
                obj = false;
            }
        }
        return obj;
    }

    public static void setObjValue(Object obj, String name, String defObj) {
        String methodName = getProSetNameMethod(name);
        try {
            Field field = getDeclaredField(obj, name);
            Class<?> fclass = field.getType();
            Class<?>[] types = {fclass};
            Method method = transferMethoder(obj, methodName, types);
            if (null != method) {
                method.invoke(obj, getStringToType(fclass, defObj));
            }
        } catch (Exception e) {
        }
    }

    public static Object getStringToType(Class<?> typeClass, String value) {
        Object obj = null;
        if (typeClass.equals(String.class)) {
            if (null == value) {
                obj = "";
            } else {
                obj = String.valueOf(value);
            }
        } else if (typeClass.equals(Double.class)) {
            if (null == value) {
                obj = 0D;
            } else {
                obj = Double.valueOf(value);
            }
        } else if (typeClass.equals(Integer.class)) {
            if (null == value) {
                obj = 0;
            } else {
                obj = Integer.valueOf(value).intValue();
            }
        } else if (typeClass.equals(Date.class)) {
            if (null == value) {
                obj = null;
            } else {
                obj = DateUtil.getDateToString(value, DateUtil.DATESHOWFORMAT);
            }
        } else if (typeClass.equals(Long.class)) {
            if (null == value) {
                obj = 0L;
            } else {
                obj = Double.valueOf(value).longValue();
            }
        }
        return obj;
    }

    public static String changeDoubleToStr(Object obj, String property, Object valueObj) {
        String value = "";
        if (null != valueObj && "" != valueObj) {
            value = valueObj.toString();
            Field field;
            try {
                field = BeanUtil.getDeclaredField(obj, property);
                Class<?> fclass = field.getType();
                if (fclass.getName().indexOf("Double") >= 0) {
                    if (!StringUtil.isNumber(valueObj.toString())) {
                        DecimalFormat df = new DecimalFormat("#.##");
                        value = df.format(Double.parseDouble(valueObj.toString()));
                    }
                }
            } catch (NoSuchFieldException e) {
            }
        }
        return value;
    }

    public static void main(String[] args) {
        getStringToType(Double.class, "1");
    }

    /**
     * 页面对象转化为bean created 2015-12-14
     *
     * @param _request
     *            HttpServletRequest
     *@param m_Object
     *            实体类对象
     *@return 实体类对象
     */
//    public static Object createObjectsFromPage(HttpServletRequest _request, Object m_Object) {
//        try {
//            Class<?> classObject = m_Object.getClass();
//            Field[] mField = classObject.getDeclaredFields();
//            Map<String,String> mValue = new HashMap<String, String>();
//            for (int i = 0; i < mField.length; i++) {
//                try {
//                    if (_request.getParameter(mField[i].getName().toString()) == null) {
//                        continue;
//                    }
//                    String mKey = mField[i].getName().toString();
//                    String mSingleValue = _request.getParameter(mKey);
//                    if ((mSingleValue == null) || (mSingleValue.toLowerCase().equals("null"))) {
//                        mSingleValue = "";
//                    }
//                    mValue.put(mKey, mSingleValue.trim());
//                } catch (Exception novalue) {
//                    novalue.printStackTrace();
//                }
//            }
//            return createSingleObject(m_Object, mValue);
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//        return null;
//    }

    /**
     *页面对象转化为bean created 2015-12-14
     *
     * @param _request
     *            HttpServletRequest
     *@param _className
     *            实体类名称
     *@return 实体类对象
     */
//    public static Object createObjectsFromPage(HttpServletRequest _request, String _className) {
//        try {
//            Object mObject = Class.forName(_className).newInstance();
//            Class<?> classObject = mObject.getClass();
//            Field[] mField = classObject.getDeclaredFields();
//            Map<String, String> mValue = new HashMap<String, String>();
//            for (int i = 0; i < mField.length; i++) {
//                try {
//                    if (_request.getParameter(mField[i].getName().toString().toLowerCase()) == null) {
//                        continue;
//                    }
//                    String mKey = mField[i].getName().toString();
//                    String mSingleValue = _request.getParameter(mKey.toLowerCase());
//                    if ((mSingleValue == null) || (mSingleValue.toLowerCase().equals("null"))) {
//                        mSingleValue = "";
//                    }
//                    mValue.put(mKey, mSingleValue.trim());
//                } catch (Exception novalue) {
//                    novalue.printStackTrace();
//                }
//            }
//            return createSingleObject(_className, mValue);
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//        return null;
//    }

    /**
     * 把指定前缀的页面参数转化为bean created 2015-12-15
     *
     * @param _request
     *            HttpServletRequest
     *@param _className
     *            实体类名称
     *@param preFix
     *            页面参数名称前缀
     *@return 实体类对象
     */
//    public static Object createObjectsFromPage(HttpServletRequest _request, String _className,
//        String preFix) {
//        try {
//            Object mObject = Class.forName(_className).newInstance();
//            Class<?> classObject = mObject.getClass();
//            Field[] mField = classObject.getDeclaredFields();
//            Map<String, String> mValue = new HashMap<String, String>();
//            for (int i = 0; i < mField.length; i++) {
//                try {
//                    if (_request
//                        .getParameter(preFix + mField[i].getName().toString().toLowerCase()) != null) {
//                        String mKey = mField[i].getName().toString();
//                        String mSingleValue = _request.getParameter(preFix + mKey.toLowerCase());
//                        mValue.put(mKey, mSingleValue.trim());
//                    }
//                } catch (Exception novalue) {
//                    novalue.printStackTrace();
//                }
//            }
//            return createSingleObject(_className, mValue);
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//        return null;
//    }

    /**
     * hashmap转化为bean
     *
     * @param _className 类名
     * @param _hashMap   hashMap
     * @return 实体类对象
     * @throws Exception 转化异常抛出
     * @author cj created 2015-12-15
     */
    @SuppressWarnings("unused")
    private static Object createSingleObject(String _className, Map<String, String> _hashMap) throws Exception {
        Object obj = Class.forName(_className).newInstance();

        BeanUtil.createSingleObject(obj, _hashMap);

        return obj;
    }

    /**
     * hashmap转化为bean
     *
     * @param m_Object 实体类对象
     * @param _hashMap hashMap
     * @return 实体类对象
     * @throws Exception 转化异常抛出
     * @author cj created 2015-12-15
     */
    private static Object createSingleObject(Object m_Object, Map<String, String> _hashMap) throws Exception {
        BeanUtil.createSingleObject(m_Object, _hashMap);
        return m_Object;
    }

    /**
     * 对象转化为hashmap
     *
     * @param mObject 对象
     * @param data    Map
     * @return Map
     * @throws Exception 抛出转化异常
     * @author cj created 2015-12-15
     */
    public static Map<String, Object> createHashMapByObject(Object mObject, Map<String, Object> data)
            throws Exception {
        Class<?> classObject = mObject.getClass();
        Field[] mField = classObject.getDeclaredFields();
        for (int i = 0; i < mField.length; i++) {
            String colName = mField[i].getName();
            data.put(colName, org.springframework.beans.BeanUtils.getPropertyDescriptor(classObject, colName));
        }
        return data;
    }
}
